open Constant

module Data = Sqlite3.Data

let check_ok db result msg =	
	if result <> Sqlite3.Rc.OK  & result <> Sqlite3.Rc.DONE 
	then begin
		let internal_msg = try " " ^ Sqlite3.errmsg db with _ -> "" in
		failwith (msg ^  " " ^Sqlite3.Rc.to_string result ^ internal_msg)
	end
	
let check_has_next result msg = 
	begin match result with
		| Sqlite3.Rc.OK -> true
		| Sqlite3.Rc.ROW -> true
		| Sqlite3.Rc.DONE -> false
		| _ -> failwith (msg ^ Sqlite3.Rc.to_string result)
	end
	
let _TABLE_CREATION = "create table pages (no INTEGER PRIMARY KEY, camera_path TEXT, camera_file TEXT, thumbname TEXT, loaded TEXT)"
let _TABLE_CONFIG_CREATION = "create table config (key TEXT PRIMARY KEY, value TEXT)"
let _TABLE_LOAD = "select * from pages"
let _TABLE_INSERT = "insert or replace into pages (no,camera_path,camera_file,thumbname,loaded) values (?,?,?,?,?)"
let _TABLE_DELETE = "delete from pages where no=?"
let _TABLE_SHIFT = "update pages set no=no+? where no >= ?"
let _CONFIG_SET = "insert or replace into config  (key,value) values (?,?)"
let _CONFIG_GET = "select value from config where key = ?"

let recover_db db pages = 
	let callback = function
	| [|n; folder; name; thumbname; loaded |] ->
		  let path = { Gphoto2.folder = folder; Gphoto2.name = name } in
		  let page = {camera_path = path; thumbname = thumbname; loaded = (if loaded = "-" then None else Some loaded) } in
			EArray.set pages (int_of_string n) (Some page)
	| _ -> print_endline "incorect recover" in
  check_ok db (Sqlite3.exec_not_null_no_headers db callback _TABLE_LOAD) "Database recovery failed";
	(* Il faut absolument assurer la parité de la base de donnée, sinon tout est inversé *)
	let size = EArray.get_size pages in
	if (size mod 2 = 1) then EArray.insert pages size
		

let open_db dbname pages =
	let is_new =  not (Sys.file_exists dbname) in 
	let db = Sqlite3.db_open dbname in
	if is_new then begin
		check_ok db (Sqlite3.exec db _TABLE_CREATION) "database creation 1";
		check_ok db (Sqlite3.exec db _TABLE_CONFIG_CREATION) "database creation 2"
	end else recover_db db pages;
	db

let begin_transaction db = check_ok db (Sqlite3.exec db "begin transaction") "begin transaction"	
let end_transaction db = check_ok db (Sqlite3.exec db "commit transaction") "end transaction"

let delete_from_db db i = 
	let stmt = Sqlite3.prepare db _TABLE_DELETE in
	check_ok db (Sqlite3.bind stmt 1 (Data.INT (Int64.of_int i))) "bind 0";
	Sqlite3.step stmt 
	
let update_db db i page =
	let stmt = Sqlite3.prepare db _TABLE_INSERT in
	check_ok db (Sqlite3.bind stmt 1 (Data.INT (Int64.of_int i))) "bind 1";
	check_ok db (Sqlite3.bind stmt 2 (Data.TEXT page.camera_path.Gphoto2.folder)) "bind 2";
	check_ok db (Sqlite3.bind stmt 3 (Data.TEXT page.camera_path.Gphoto2.name)) "bind 3";
	check_ok db (Sqlite3.bind stmt 4 (Data.TEXT page.thumbname))"bind 4";
	check_ok db (Sqlite3.bind stmt 5 (Data.TEXT (match page.loaded with Some file -> file | None -> "-"))) "bind 5";
	Sqlite3.step stmt

let save_db db pages =
	let callback i = function
		| None -> ignore (delete_from_db db  i)
		| Some page -> ignore (update_db db i page) in
	EArray.iterate pages callback
	 
let shift_db db v n = 
	let stmt = Sqlite3.prepare db _TABLE_SHIFT in 
	check_ok db (Sqlite3.bind stmt 1 (Data.INT (Int64.of_int n))) "shift 1";
	check_ok db (Sqlite3.bind stmt 2 (Data.INT (Int64.of_int v))) "shift 2"; 
	check_ok db (Sqlite3.step stmt) "shift exec"

let get_config db key = 
	let stmt = Sqlite3.prepare db _CONFIG_GET in
	check_ok db (Sqlite3.bind stmt 1 (Data.TEXT key)) "get_config 1";
	if (check_has_next (Sqlite3.step stmt) "get_config e") then begin
	  let result = Sqlite3.column stmt 0 in
		check_ok db (Sqlite3.finalize stmt) "get_config f";
		match result with Sqlite3.Data.TEXT text -> Some text | _ -> None
	end else None

		
let set_config db key value = 
	let stmt = Sqlite3.prepare db _CONFIG_SET in
	check_ok db (Sqlite3.bind stmt 1 (Data.TEXT key)) "set_config 1";
	check_ok db (Sqlite3.bind stmt 2 (Data.TEXT value)) "set_config 2";
	check_ok db (Sqlite3.step stmt) "set_config e";
	check_ok db (Sqlite3.finalize stmt) "set_config f"
	
	let get_config_float db key = match get_config db key with Some text -> Some (float_of_string text) | None -> None

	let set_config_float db key value= set_config db key (string_of_float value)
	